﻿'TODO: Document Bar Gauge
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.ComponentModel
Imports System.ComponentModel.Design

<DefaultEvent("Click"), ToolboxBitmap(GetType(BarGauge), "BarGauge.bmp")> _
Public Class BarGauge

#Region "Defaults"
    Private Const def_BarValue As Double = 80
    Private def_BarColor As Color = Color.DarkGoldenrod

    Private Const def_TextLabelPosition As TextLabelPositions = TextLabelPositions.PositionToLeft

    Private def_HeatLevelStyle As HeatLevel.HeatLevelStyles = HeatLevel.HeatLevelStyles.DoubleDecreasingToMiddle
    Private Const def_HeatLevelHeight As Integer = 15

    Private Const def_HeatLevel1_StartValue As Double = -100
    Private Const def_HeatLevel1_EndValue As Double = 0
    Private def_HeatLevel1_StartColor As Color = Color.Red
    Private Const def_HeatLevel1_Threshold1_Value As Double = -75
    Private Const def_HeatLevel1_Threshold2_Value As Double = -60
    Private def_HeatLevel1_Threshold1_Color As Color = Color.Gold
    Private def_HeatLevel1_Threshold2_Color As Color = Color.MediumBlue

    Private Const def_HeatLevel2_StartValue As Double = 0
    Private Const def_HeatLevel2_EndValue As Double = 100
    Private def_HeatLevel2_StartColor As Color = Color.MediumBlue
    Private Const def_HeatLevel2_Threshold1_Value As Double = 60
    Private Const def_HeatLevel2_Threshold2_Value As Double = 75
    Private def_HeatLevel2_Threshold1_Color As Color = Color.Gold
    Private def_HeatLevel2_Threshold2_Color As Color = Color.Red
#End Region

#Region "Private Attributes"
    Private WithEvents _Background As New BackgroundType(Me)
    Private WithEvents _Axis As New AxisType(Me)

    Private Const def_ForeColor As String = "White"

    Private _BarColor As Color = def_BarColor
    Private _BarValue As Double = def_BarValue
    Private _BarLabelPosition As TextLabelPositions = def_TextLabelPosition


    Private _HeatLevelStyle As HeatLevel.HeatLevelStyles = def_HeatLevelStyle
    Private _HeatLevelHeight As Integer = def_HeatLevelHeight


    Private WithEvents _HeatLevel1 As New HeatLevel(def_HeatLevel1_StartValue, def_HeatLevel1_EndValue, def_HeatLevel1_StartColor, _
                                         def_HeatLevel1_Threshold1_Value, def_HeatLevel1_Threshold2_Value, _
                                         def_HeatLevel1_Threshold1_Color, def_HeatLevel1_Threshold2_Color)

    Private WithEvents _HeatLevel2 As New HeatLevel(def_HeatLevel2_StartValue, def_HeatLevel2_EndValue, def_HeatLevel2_StartColor, _
                                         def_HeatLevel2_Threshold1_Value, def_HeatLevel2_Threshold2_Value, _
                                         def_HeatLevel2_Threshold1_Color, def_HeatLevel2_Threshold2_Color)

#End Region

#Region "Transparency Support"
    Private Const WS_EX_TRANSPARENT As Int16 = &H20

    Friend Sub SetTransparencyMode(ByVal IsOn As Boolean)
        If IsOn Then
            'Support transparency - turn off double-buffering
            Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, False) 'Turn double-buffering off for transparency
            Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
            Me.SetStyle(ControlStyles.Opaque, True)
        Else
            'No transparency support - enable double-buffering
            Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True) 'Turn double-buffering off for transparency
            Me.SetStyle(ControlStyles.SupportsTransparentBackColor, False)
            Me.SetStyle(ControlStyles.Opaque, False)
        End If
        If Me.Parent Is Nothing Then
            Me.Refresh()
        Else
            Me.Parent.Refresh()
        End If
    End Sub
    Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or WS_EX_TRANSPARENT
            Return cp
        End Get
    End Property
#End Region

#Region "Public Enumerations"

    Public Enum TextLabelPositions As Integer
        PositionToLeft = 0
        PositionToRight = 1
        None = 3
    End Enum

#End Region

#Region "Hidden Properties"
    <Browsable(False)> _
    Shadows Property BackColor() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
    <Browsable(False)> _
    Shadows Property BackgroundImage() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
    <Browsable(False)> _
    Shadows Property BackgroundImageLayout() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
#End Region

#Region "Public Interface"
    Public Sub New()
        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.ResizeRedraw, True)
        Me.SetStyle(ControlStyles.UserPaint, True)
        SetTransparencyMode(False)
        Me.Size = New Size(300, 60)
        Me.Padding = New Padding(5, 5, 10, 5)
        Me.Invalidate()
    End Sub

    Shadows Property Text() As String
        Get
            Return MyBase.Text
        End Get
        Set(ByVal value As String)
            MyBase.Text = value
            Me.Invalidate()
        End Set
    End Property
    Shadows Sub ResetText()
        MyBase.Text = Nothing
    End Sub
    Shadows Function ShouldSerializeText() As Boolean
        Return Not (MyBase.Text.Equals(Nothing))
    End Function

    <Category("Appearance"), _
    Description("The position of the label defined in the Text property. Set this property to None to stop display of the label."), _
    BrowsableAttribute(True)> _
    Public Property TextLabelPosition() As TextLabelPositions
        Get
            Return _BarLabelPosition
        End Get
        Set(ByVal value As TextLabelPositions)
            _BarLabelPosition = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetTextLabelPosition()
        TextLabelPosition = def_TextLabelPosition
    End Sub
    Public Function ShouldSerializeTextLabelPosition() As Boolean
        Return Not (TextLabelPosition.Equals(def_TextLabelPosition))
    End Function


    <Category("Appearance"), TypeConverter(GetType(BackgroundTypeConverter))> _
    Public Property Background() As BackgroundType
        Get
            Return _Background
        End Get
        Set(ByVal value As BackgroundType)
            _Background = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBackground()
        _Background.Reset()
    End Sub
    Public Function ShouldSerializeBackground() As Boolean
        Return _Background.ShouldSerialize()
    End Function

    <Category("Gauge"), TypeConverter(GetType(AxisTypeConverter))> _
    Public Property Axis() As AxisType
        Get
            Return _Axis
        End Get
        Set(ByVal value As AxisType)
            _Axis = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetAxis()
        _Axis.Reset()
    End Sub
    Public Function ShouldSerializeAxis() As Boolean
        Return _Axis.ShouldSerialize()
    End Function


    <Category("Gauge"), _
           Description("The bar gauge value."), _
            DefaultValue(def_BarValue), _
            BrowsableAttribute(True)> _
        Public Property Value() As Double
        Get
            Return _BarValue
        End Get
        Set(ByVal value As Double)
            _BarValue = value
            Me.Invalidate()
        End Set
    End Property

    <Category("Gauge"), _
    Description("The color of the gauge bar."), _
    BrowsableAttribute(True)> _
    Public Property BarColor() As Color
        Get
            Return _BarColor
        End Get
        Set(ByVal value As Color)
            _BarColor = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBarColor()
        BarColor = def_BarColor
    End Sub
    Public Function ShouldSerializeBarColor() As Boolean
        Return Not (BarColor.Equals(def_BarColor))
    End Function


    <Category("Heat Levels"), _
        Description("Defines the Heat Level style. Note that for a single Heat Level, the HeatLevel1 definitions are used and the HeatLevel2 definitions are ignored."), _
        BrowsableAttribute(True)> _
    Public Property HeatLevelStyle() As HeatLevel.HeatLevelStyles
        Get
            Return _HeatLevelStyle
        End Get
        Set(ByVal value As HeatLevel.HeatLevelStyles)
            _HeatLevelStyle = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetHeatLevelStyle()
        HeatLevelStyle = def_HeatLevelStyle
    End Sub
    Public Function ShouldSerializeHeatLevelStyle() As Boolean
        Return Not (HeatLevelStyle.Equals(def_HeatLevelStyle))
    End Function

    <Category("Heat Levels"), _
        Description("The Height of the Heat Levels (if displayed)."), _
        DefaultValue(def_HeatLevelHeight), _
        BrowsableAttribute(True)> _
    Public Property HeatLevelHeight() As Integer
        Get
            Return _HeatLevelHeight
        End Get
        Set(ByVal value As Integer)
            If value < 1 Then Throw New Exception("Heat Level height must be at least 1.")
            _HeatLevelHeight = value
            Me.Invalidate()
        End Set
    End Property


    <Category("Heat Levels"), TypeConverter(GetType(HeatLevelTypeConverter)), _
        Description("Heat Level 1 definition. Used in Single or Dual heat level styles.")> _
    Public Property HeatLevel1() As HeatLevel
        Get
            Return _HeatLevel1
        End Get
        Set(ByVal value As HeatLevel)
            _HeatLevel1 = value
        End Set
    End Property
    Public Sub ResetHeatLevel1()
        _HeatLevel1.Reset()
    End Sub
    Public Function ShouldSerializeHeatLevel1() As Boolean
        Return _HeatLevel1.ShouldSerialize
    End Function

    <Category("Heat Levels"), TypeConverter(GetType(HeatLevelTypeConverter)), _
        Description("Heat level 2 definition. Ignored in Single heat level styles.")> _
    Public Property HeatLevel2() As HeatLevel
        Get
            Return _HeatLevel2
        End Get
        Set(ByVal value As HeatLevel)
            _HeatLevel2 = value
        End Set
    End Property
    Public Sub ResetHeatLevel2()
        _HeatLevel2.Reset()
    End Sub
    Public Function ShouldSerializeHeatLevel2() As Boolean
        Return _HeatLevel2.ShouldSerialize
    End Function
#End Region

#Region "Private Methods"
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        'Add your custom paint code here
        Dim myG As Graphics = e.Graphics
        myG.SmoothingMode = SmoothingMode.HighQuality

        Dim HeatLevelBarGap As Single = 3
        Dim HeatLevelHeight As Single = CSng(_HeatLevelHeight)
        Dim AxisLabelBarGap As Single = 3
        Dim TopPadding As Single = Me.Padding.Top
        Dim BottomPadding As Single = Me.Padding.Bottom
        Dim LeftPadding As Single = Me.Padding.Left
        Dim RightPadding As Single = Me.Padding.Right
        Dim LabelPadding As Single = 3

        'Determine Various Areas where things will be drawn
        Dim AxisLabelHeight As Single = 0
        Select Case _Axis.LabelDisplayStyle
            Case AxisType.LabelDisplayStyles.NoLabels
                'No Axis Labels
                AxisLabelHeight = 0
                AxisLabelBarGap = 0
            Case Else
                'Display Axis Labels
                AxisLabelHeight = myG.MeasureString("AAA", _Axis.Font).Height
        End Select

        If _HeatLevelStyle = HeatLevel.HeatLevelStyles.None Then
            HeatLevelBarGap = 0
            HeatLevelHeight = 0
        End If

        'Calculate region available for Bar gauge itself / Bar Label
        Dim BarH As Single = CSng(Me.ClientSize.Height) - TopPadding - HeatLevelHeight - HeatLevelBarGap - AxisLabelHeight - AxisLabelBarGap - BottomPadding
        Dim BarY As Single = TopPadding + HeatLevelHeight + HeatLevelBarGap
        Dim BarW As Single = 0
        Dim BarX As Single = 0
        Dim BarLabelX As Single = 0
        Dim BarLabelY As Single = 0
        Dim BarLabelH As Single = 0
        Dim BarLabelW As Single = 0

        Select Case _BarLabelPosition
            Case TextLabelPositions.None
                BarX = LeftPadding
                BarW = Me.ClientSize.Width - LeftPadding - RightPadding

                BarLabelX = 0
                BarLabelY = 0
                BarLabelW = 1
                BarLabelH = 1
            Case TextLabelPositions.PositionToLeft
                BarX = LeftPadding + myG.MeasureString(Me.Text, Me.Font).Width + LabelPadding
                BarW = Me.ClientSize.Width - RightPadding - BarX

                BarLabelW = myG.MeasureString(Me.Text, Me.Font).Width
                BarLabelH = myG.MeasureString(Me.Text, Me.Font).Height
                BarLabelX = LeftPadding
                BarLabelY = BarY + (BarH - BarLabelH) / 2
            Case TextLabelPositions.PositionToRight
                BarX = LeftPadding
                BarW = Me.ClientSize.Width - RightPadding - LeftPadding - myG.MeasureString(Me.Text, Me.Font).Width - LabelPadding

                BarLabelW = myG.MeasureString(Me.Text, Me.Font).Width
                BarLabelH = myG.MeasureString(Me.Text, Me.Font).Height
                BarLabelX = BarX + BarW + LabelPadding
                BarLabelY = BarY + (BarH - BarLabelH) / 2
        End Select
        Dim BarGaugeRectF As New RectangleF(BarX, BarY, BarW, BarH)
        Dim BarLabelRectF As New RectangleF(BarLabelX, BarLabelY, BarLabelW, BarLabelH)
        Dim AxisRectF As New RectangleF(BarX, BarY + BarH + AxisLabelBarGap, BarW, AxisLabelHeight)

        'Calculate region for available for HeatLevels
        Dim HeatLevelRectF As RectangleF
        If _HeatLevelStyle <> HeatLevel.HeatLevelStyles.None Then
            Dim HeatLevelTopY As Single = TopPadding
            Dim HeatLevelLeftX As Single = BarGaugeRectF.X
            Dim HeatLevelWidth As Single = BarGaugeRectF.Width

            HeatLevelRectF = New RectangleF(HeatLevelLeftX, HeatLevelTopY, HeatLevelWidth, HeatLevelHeight)
        Else
            HeatLevelRectF = New RectangleF(0, 0, 1, 1) ' Will not need this region
        End If

        'Draw Gauge Background
        DrawGaugeBackground(myG, Me.ClientRectangle)

        'Draw HeatLevels
        If _HeatLevelStyle <> HeatLevel.HeatLevelStyles.None Then DrawHeatLevels(myG, HeatLevelRectF)

        'Draw Gauge
        DrawGauge(myG, BarGaugeRectF)

        'Draw Axis
        If _Axis.LabelDisplayStyle <> AxisType.LabelDisplayStyles.NoLabels Then DrawAxisLabels(myG, AxisRectF)

        'Dar Bar Label
        If _BarLabelPosition <> TextLabelPositions.None Then DrawBarLabel(myG, BarLabelRectF)

    End Sub

    Private Sub DrawGaugeBackground(ByRef G As Graphics, ByVal Rect As Rectangle)
        If _Background.FillStyle = BackgroundType.FillStyles.Gradient Then
            Dim myBrush As LinearGradientBrush
            myBrush = New LinearGradientBrush(Rect, _Background.BackColor1, _Background.BackColor2, _Background.FillGradientStyle)
            G.FillRectangle(myBrush, Rect)
        Else
            G.FillRectangle(New SolidBrush(_Background.BackColor1), Rect)
        End If
    End Sub
    Private Sub DrawHeatLevels(ByRef G As Graphics, ByVal RectF As RectangleF)
        'Heat levels must be drawn according to how the gauge will scale
        Select Case _HeatLevelStyle
            Case HeatLevel.HeatLevelStyles.SingleIncreasing
                Dim StartX As Single = TransformValToScaledPt(_HeatLevel1.StartValue, RectF, _Axis.Min, _Axis.Max)
                Dim EndX As Single = TransformValToScaledPt(_HeatLevel1.EndValue, RectF, _Axis.Min, _Axis.Max)

                Dim Pt1 As PointF = New PointF(StartX, RectF.Y + RectF.Height)
                Dim Pt2 As PointF = New PointF(EndX, RectF.Y)
                Dim Pt3 As PointF = New PointF(EndX, RectF.Y + RectF.Height)

                'Define gradient brush
                Dim HeatBrush As New LinearGradientBrush(RectF, _HeatLevel1.StartColor, _HeatLevel1.Threshold2.Color, LinearGradientMode.Horizontal)
                Dim colorBlend As New ColorBlend()

                Dim Col1 As Color = _HeatLevel1.StartColor
                Dim Col2 As Color = _HeatLevel1.Threshold1.Color
                Dim Col3 As Color = _HeatLevel1.Threshold2.Color


                Dim Offset As Single = CSng(0 - _HeatLevel1.StartValue)

                Dim Pos1 As Single = 0
                Dim Pos2 As Single = CSng((_HeatLevel1.Threshold1.Value + Offset) / (_HeatLevel1.EndValue - _HeatLevel1.StartValue))
                Dim Pos3 As Single = CSng((_HeatLevel1.Threshold2.Value + Offset) / (_HeatLevel1.EndValue - _HeatLevel1.StartValue))
                Dim Pos4 As Single = 1

                colorBlend.Colors = New Color() {Col1, Col1, Col2, Col3}
                colorBlend.Positions = New Single() {Pos1, Pos2, Pos3, Pos4}
                HeatBrush.InterpolationColors = colorBlend

                'Draw Polygon
                G.FillPolygon(HeatBrush, New PointF() {Pt1, Pt2, Pt3})

            Case HeatLevel.HeatLevelStyles.SingleDecreasing
                Dim StartX As Single = TransformValToScaledPt(_HeatLevel1.StartValue, RectF, _Axis.Min, _Axis.Max)
                Dim EndX As Single = TransformValToScaledPt(_HeatLevel1.EndValue, RectF, _Axis.Min, _Axis.Max)


                Dim Pt1 As PointF = New PointF(StartX, RectF.Y)
                Dim Pt2 As PointF = New PointF(StartX, RectF.Y + RectF.Height)
                Dim Pt3 As PointF = New PointF(EndX, RectF.Y + RectF.Height)


                'Define gradient brush
                Dim HeatBrush As New LinearGradientBrush(RectF, Me._HeatLevel1.StartColor, Me._HeatLevel1.Threshold2.Color, LinearGradientMode.Horizontal)
                Dim colorBlend As New ColorBlend()

                Dim Col1 As Color = _HeatLevel1.StartColor
                Dim Col2 As Color = _HeatLevel1.Threshold1.Color
                Dim Col3 As Color = _HeatLevel1.Threshold2.Color


                Dim Offset As Single = CSng(0 - _HeatLevel1.StartValue)

                Dim Pos1 As Single = 0
                Dim Pos2 As Single = CSng((_HeatLevel1.Threshold1.Value + Offset) / (_HeatLevel1.EndValue - _HeatLevel1.StartValue))
                Dim Pos3 As Single = CSng((_HeatLevel1.Threshold2.Value + Offset) / (_HeatLevel1.EndValue - _HeatLevel1.StartValue))
                Dim Pos4 As Single = 1

                colorBlend.Colors = New Color() {Col1, Col2, Col3, Col3}
                colorBlend.Positions = New Single() {Pos1, Pos2, Pos3, Pos4}
                HeatBrush.InterpolationColors = colorBlend


                G.FillPolygon(HeatBrush, New PointF() {Pt1, Pt2, Pt3})

            Case HeatLevel.HeatLevelStyles.DoubleDecreasingToMiddle
                'Left Heat Level
                Dim LeftStartX As Single = TransformValToScaledPt(_HeatLevel1.StartValue, RectF, _Axis.Min, _Axis.Max)
                Dim LeftEndX As Single = TransformValToScaledPt(_HeatLevel1.EndValue, RectF, _Axis.Min, _Axis.Max)

                Dim Pt1 As PointF = New PointF(LeftStartX, RectF.Y)
                Dim Pt2 As PointF = New PointF(LeftStartX, RectF.Y + RectF.Height)
                Dim Pt3 As PointF = New PointF(LeftEndX, RectF.Y + RectF.Height)

                'Define Left gradient brush
                Dim HeatBrush As New LinearGradientBrush(RectF, Me._HeatLevel1.StartColor, Me._HeatLevel1.Threshold2.Color, LinearGradientMode.Horizontal)
                Dim colorBlend As New ColorBlend()
                Dim Col1 As Color = _HeatLevel1.StartColor
                Dim Col2 As Color = _HeatLevel1.Threshold1.Color
                Dim Col3 As Color = _HeatLevel1.Threshold2.Color
                Dim Offset As Single = CSng(0 - _HeatLevel1.StartValue)
                Dim Pos1 As Single = 0
                Dim Pos2 As Single = CSng((_HeatLevel1.Threshold1.Value + Offset) / (_HeatLevel1.EndValue - _HeatLevel1.StartValue))
                Dim Pos3 As Single = CSng((_HeatLevel1.Threshold2.Value + Offset) / (_HeatLevel1.EndValue - _HeatLevel1.StartValue))
                Dim Pos4 As Single = 1
                colorBlend.Colors = New Color() {Col1, Col2, Col3, Col3}
                colorBlend.Positions = New Single() {Pos1, Pos2, Pos3, Pos4}
                HeatBrush.InterpolationColors = colorBlend

                'Draw Left polygon
                G.FillPolygon(HeatBrush, New PointF() {Pt1, Pt2, Pt3})

                'Right Heat Level
                Dim RightStartX As Single = TransformValToScaledPt(_HeatLevel2.StartValue, RectF, _Axis.Min, _Axis.Max)
                Dim RightEndX As Single = TransformValToScaledPt(_HeatLevel2.EndValue, RectF, _Axis.Min, _Axis.Max)

                Pt1 = New PointF(RightStartX, RectF.Y + RectF.Height)
                Pt2 = New PointF(RightEndX, RectF.Y)
                Pt3 = New PointF(RightEndX, RectF.Y + RectF.Height)

                'Define gradient brush
                HeatBrush = New LinearGradientBrush(RectF, Me._HeatLevel2.StartColor, Me._HeatLevel2.Threshold2.Color, LinearGradientMode.Horizontal)
                colorBlend = New ColorBlend()
                Col1 = _HeatLevel2.StartColor
                Col2 = _HeatLevel2.Threshold1.Color
                Col3 = _HeatLevel2.Threshold2.Color
                Offset = CSng(0 - _HeatLevel2.StartValue)
                Pos1 = 0
                Pos2 = CSng((_HeatLevel2.Threshold1.Value + Offset) / (_HeatLevel2.EndValue - _HeatLevel2.StartValue))
                Pos3 = CSng((_HeatLevel2.Threshold2.Value + Offset) / (_HeatLevel2.EndValue - _HeatLevel2.StartValue))
                Pos4 = 1
                colorBlend.Colors = New Color() {Col1, Col1, Col2, Col3}
                colorBlend.Positions = New Single() {Pos1, Pos2, Pos3, Pos4}
                HeatBrush.InterpolationColors = colorBlend

                'Draw Right Polygon
                G.FillPolygon(HeatBrush, New PointF() {Pt1, Pt2, Pt3})

            Case HeatLevel.HeatLevelStyles.DoubleIncreasingToMiddle
                'Left Heat Level
                Dim LeftStartX As Single = TransformValToScaledPt(_HeatLevel1.StartValue, RectF, _Axis.Min, _Axis.Max)
                Dim LeftEndX As Single = TransformValToScaledPt(_HeatLevel1.EndValue, RectF, _Axis.Min, _Axis.Max)

                Dim Pt1 As PointF = New PointF(LeftStartX, RectF.Y + RectF.Height)
                Dim Pt2 As PointF = New PointF(LeftEndX, RectF.Y)
                Dim Pt3 As PointF = New PointF(LeftEndX, RectF.Y + RectF.Height)

                'Define Left gradient brush
                Dim HeatBrush As New LinearGradientBrush(RectF, Me._HeatLevel1.StartColor, Me._HeatLevel1.Threshold2.Color, LinearGradientMode.Horizontal)
                Dim colorBlend As New ColorBlend()
                Dim Col1 As Color = _HeatLevel1.StartColor
                Dim Col2 As Color = _HeatLevel1.Threshold1.Color
                Dim Col3 As Color = _HeatLevel1.Threshold2.Color
                Dim Offset As Single = CSng(0 - _HeatLevel1.StartValue)
                Dim Pos1 As Single = 0
                Dim Pos2 As Single = CSng((_HeatLevel1.Threshold1.Value + Offset) / (_HeatLevel1.EndValue - _HeatLevel1.StartValue))
                Dim Pos3 As Single = CSng((_HeatLevel1.Threshold2.Value + Offset) / (_HeatLevel1.EndValue - _HeatLevel1.StartValue))
                Dim Pos4 As Single = 1
                colorBlend.Colors = New Color() {Col1, Col1, Col2, Col3}
                colorBlend.Positions = New Single() {Pos1, Pos2, Pos3, Pos4}
                HeatBrush.InterpolationColors = colorBlend

                'Draw Left polygon
                G.FillPolygon(HeatBrush, New PointF() {Pt1, Pt2, Pt3})


                'Right Heat Level
                Dim RightStartX As Single = TransformValToScaledPt(_HeatLevel2.StartValue, RectF, _Axis.Min, _Axis.Max)
                Dim RightEndX As Single = TransformValToScaledPt(_HeatLevel2.EndValue, RectF, _Axis.Min, _Axis.Max)

                Pt1 = New PointF(RightStartX, RectF.Y)
                Pt2 = New PointF(RightStartX, RectF.Y + RectF.Height)
                Pt3 = New PointF(RightEndX, RectF.Y + RectF.Height)

                'Define gradient brush
                HeatBrush = New LinearGradientBrush(RectF, Me._HeatLevel2.StartColor, Me._HeatLevel2.Threshold2.Color, LinearGradientMode.Horizontal)
                colorBlend = New ColorBlend()
                Col1 = _HeatLevel2.StartColor
                Col2 = _HeatLevel2.Threshold1.Color
                Col3 = _HeatLevel2.Threshold2.Color
                Offset = CSng(0 - _HeatLevel2.StartValue)
                Pos1 = 0
                Pos2 = CSng((_HeatLevel2.Threshold1.Value + Offset) / (_HeatLevel2.EndValue - _HeatLevel2.StartValue))
                Pos3 = CSng((_HeatLevel2.Threshold2.Value + Offset) / (_HeatLevel2.EndValue - _HeatLevel2.StartValue))
                Pos4 = 1
                colorBlend.Colors = New Color() {Col1, Col2, Col3, Col3}
                colorBlend.Positions = New Single() {Pos1, Pos2, Pos3, Pos4}
                HeatBrush.InterpolationColors = colorBlend

                'Draw Right Polygon
                G.FillPolygon(HeatBrush, New PointF() {Pt1, Pt2, Pt3})
        End Select
    End Sub
    Private Sub DrawGauge(ByRef G As Graphics, ByVal RectF As RectangleF)
        Dim BarValue As Double = _BarValue
        If BarValue < _Axis.Min Then BarValue = _Axis.Min
        If BarValue > _Axis.Max Then BarValue = _Axis.Max

        Dim BarRectF As RectangleF
        Dim BarWidth As Single = TransformValToScaledPt(BarValue, RectF, _Axis.Min, _Axis.Max) - RectF.X
        BarRectF = New RectangleF(RectF.X, RectF.Y, BarWidth, RectF.Height)

        Dim BarColor1 As Color = Color.FromArgb(Math.Max(0, _BarColor.R - 80), Math.Max(0, _BarColor.G - 80), Math.Max(0, _BarColor.B - 80))
        Dim BarColor2 As Color = Color.FromArgb(Math.Min(255, _BarColor.R + 180), Math.Min(255, _BarColor.G + 180), Math.Min(255, _BarColor.B + 180))
        Dim BrushRectF As New RectangleF(Math.Max(1, BarRectF.X - 2), Math.Max(1, BarRectF.Y - 2), Math.Max(1, BarRectF.Width + 3), Math.Max(1, BarRectF.Height + 3))
        Dim FillBrush As New LinearGradientBrush(BrushRectF, BarColor2, BarColor1, LinearGradientMode.Vertical)
        G.FillRectangle(FillBrush, BarRectF)

        Dim MajorTickPadding As Single = 2
        Dim MinorTickPadding As Single = 4
        If RectF.Height < MajorTickPadding * 2 + 2 Then MajorTickPadding = 0
        If RectF.Height < MinorTickPadding * 2 + 2 Then MinorTickPadding = MajorTickPadding

        Dim CurrGMode As SmoothingMode
        CurrGMode = G.SmoothingMode
        G.SmoothingMode = SmoothingMode.None
        Select Case _Axis.AxisStyle
            Case AxisType.AxisStyles.MajorTicksOnly
                Dim MajorStep As Single = CSng(_Axis.Max - _Axis.Min) / CSng(_Axis.MajorTicks + 1)
                If _Axis.MajorTicks > 0 Then
                    For i As Integer = 0 To _Axis.MajorTicks + 1
                        Dim MajorTickValue As Single = CSng(_Axis.Min) + CSng(i) * MajorStep
                        Dim MajorTickPt As Single = TransformValToScaledPt(MajorTickValue, RectF, _Axis.Min, _Axis.Max)
                        G.DrawLine(New Pen(_Axis.TickColor), MajorTickPt, RectF.Y + MajorTickPadding, MajorTickPt, RectF.Y + RectF.Height - MajorTickPadding)
                    Next
                End If
            Case AxisType.AxisStyles.MajorMinorTicks
                Dim MajorStep As Single = CSng(_Axis.Max - _Axis.Min) / CSng(_Axis.MajorTicks + 1)
                Dim MinorStep As Single = MajorStep / CSng(_Axis.MinorTicks + 1)
                Dim MinorStepPt As Single = RectF.Width / CSng((_Axis.MajorTicks + 1) * (_Axis.MinorTicks + 1))

                If _Axis.MajorTicks > 0 Then
                    For i As Integer = 0 To _Axis.MajorTicks + 1

                        Dim MajorTickValue As Single = CSng(_Axis.Min) + CSng(i) * MajorStep
                        Dim MajorTickPt As Single = TransformValToScaledPt(MajorTickValue, RectF, _Axis.Min, _Axis.Max)

                        G.DrawLine(New Pen(_Axis.TickColor), MajorTickPt, RectF.Y + MajorTickPadding, MajorTickPt, RectF.Y + RectF.Height - MajorTickPadding)

                        If i < _Axis.MajorTicks + 1 Then
                            If _Axis.MinorTicks > 0 Then
                                For j = 1 To _Axis.MinorTicks
                                    G.DrawLine(New Pen(_Axis.TickColor), MajorTickPt + (MinorStepPt * j), RectF.Y + MinorTickPadding, MajorTickPt + (MinorStepPt * j), RectF.Y + RectF.Height - MinorTickPadding)
                                Next
                            End If
                        End If
                    Next
                End If
            Case Else
                'No tick marks
        End Select
        G.SmoothingMode = CurrGMode
    End Sub
    Private Sub DrawAxisLabels(ByVal G As Graphics, ByVal RectF As RectangleF)
        'G.FillRectangle(Brushes.Red, RectF)
        Dim StartLbl As String = CStr(_Axis.Min)
        Dim EndLbl As String = CStr(_Axis.Max)

        Select Case _Axis.LabelDisplayStyle
            Case AxisType.LabelDisplayStyles.EndPointsOnly
                'Display End Points only
                Dim X As Single = CenterLabelToPt(G, StartLbl, _Axis.Font, RectF.X)
                G.DrawString(StartLbl, _Axis.Font, New SolidBrush(_Axis.ForeColor), X, RectF.Y)

                X = CenterLabelToPt(G, EndLbl, _Axis.Font, RectF.X + RectF.Width)
                G.DrawString(EndLbl, _Axis.Font, New SolidBrush(_Axis.ForeColor), X, RectF.Y)
            Case AxisType.LabelDisplayStyles.MajorTickLabelsOnly
                'Display Major Labels only
                If _Axis.MajorTicks > 0 Then
                    Dim MajorStep As Single = CSng(_Axis.Max - _Axis.Min) / CSng(_Axis.MajorTicks + 1)
                    For i As Integer = 1 To _Axis.MajorTicks
                        Dim TickValue As Single = CSng(_Axis.Min) + CSng(i) * MajorStep
                        Dim TickLabel As String = CInt(TickValue).ToString().Trim()
                        Dim TickPt As Single = TransformValToScaledPt(TickValue, RectF, _Axis.Min, _Axis.Max)
                        Dim TickX As Single = CenterLabelToPt(G, TickLabel, _Axis.Font, TickPt)
                        G.DrawString(TickLabel, _Axis.Font, New SolidBrush(_Axis.ForeColor), TickX, RectF.Y)
                    Next
                End If
            Case AxisType.LabelDisplayStyles.All
                'Display End and Major Labels
                Dim X As Single = CenterLabelToPt(G, StartLbl, _Axis.Font, RectF.X)
                G.DrawString(StartLbl, _Axis.Font, New SolidBrush(Me._Axis.ForeColor), X, RectF.Y)

                X = CenterLabelToPt(G, EndLbl, _Axis.Font, RectF.X + RectF.Width)
                G.DrawString(EndLbl, _Axis.Font, New SolidBrush(Me._Axis.ForeColor), X, RectF.Y)

                If _Axis.MajorTicks > 0 Then
                    Dim MajorStep As Single = CSng(_Axis.Max - _Axis.Min) / CSng(_Axis.MajorTicks + 1)
                    For i As Integer = 1 To _Axis.MajorTicks
                        Dim TickValue As Single = CSng(_Axis.Min) + CSng(i) * MajorStep
                        Dim TickLabel As String = CInt(TickValue).ToString()
                        Dim TickPt As Single = TransformValToScaledPt(TickValue, RectF, _Axis.Min, _Axis.Max)
                        Dim TickX As Single = CenterLabelToPt(G, TickLabel, _Axis.Font, TickPt)
                        G.DrawString(TickLabel, _Axis.Font, New SolidBrush(_Axis.ForeColor), TickX, RectF.Y)
                    Next
                End If
        End Select
    End Sub
    Private Sub DrawBarLabel(ByRef G As Graphics, ByVal RectF As RectangleF)
        Select Case _BarLabelPosition
            Case TextLabelPositions.PositionToLeft
                Dim TextHeight As Single = G.MeasureString(Me.Text, Me.Font).Height
                Dim TextWidth As Single = G.MeasureString(Me.Text, Me.Font).Width
                Dim TextX As Single = RectF.X + RectF.Width - TextWidth
                G.DrawString(Me.Text, Me.Font, New SolidBrush(Me.ForeColor), TextX, RectF.Y)
            Case TextLabelPositions.PositionToRight
                G.DrawString(Me.Text, Me.Font, New SolidBrush(Me.ForeColor), RectF.X, RectF.Y)
        End Select

    End Sub

    Private Function CenterLabelToPt(ByVal G As Graphics, ByVal Label As String, ByVal LabelFont As Font, ByVal CenterPt As Single) As Single
        Dim TextWidth As Single = G.MeasureString(Label, Font).Width
        Return CenterPt - (TextWidth / 3)
    End Function
    Private Function TransformValToScaledPt(ByVal Value As Double, ByVal RectF As RectangleF, ByVal ScaleMin As Double, ByVal ScaleMax As Double) As Single
        Return RectF.X + RectF.Width * CSng((Value - _Axis.Min) / (_Axis.Max - _Axis.Min))
    End Function
#End Region

#Region "Private Event Handlers"
    Private Sub AxisProperties_PropertyChanged(ByVal PropertyName As String) Handles _Axis.PropertyChanged
        Me.Invalidate()
    End Sub
    Private Sub Background_PropertyChanged(ByVal PropertyName As String) Handles _Background.PropertyChanged
        Select Case PropertyName
            Case "BackColor1"
                If Me.Parent Is Nothing Then
                    Me.Refresh()
                Else
                    Me.Parent.Refresh()
                End If
            Case Else
                Me.Invalidate()
        End Select
    End Sub
    Private Sub _HeatLevel1_PropertyChanged(ByVal PropertyName As String) Handles _HeatLevel1.PropertyChanged
        Me.Invalidate()
    End Sub
    Private Sub _HeatLevel2_PropertyChanged(ByVal PropertyName As String) Handles _HeatLevel2.PropertyChanged
        Me.Invalidate()
    End Sub
#End Region

#Region "Background Type"
    Public Class BackgroundType
        'Defaults
        Private Const def_SupportTransparency As Boolean = False

        Private def_BackColor1 As Color = Color.SkyBlue
        Private def_BackColor2 As Color = Color.RoyalBlue
        Private def_FillStyle As FillStyles = FillStyles.Gradient
        Private def_FillGradientStyle As LinearGradientMode = LinearGradientMode.Horizontal


        'Private Properties
        Private _SupportTransparency As Boolean = def_SupportTransparency

        Private _BackColor1 As Color = def_BackColor1
        Private _BackColor2 As Color = def_BackColor2
        Private _FillStyle As FillStyles = def_FillStyle
        Private _GradientOrientation As LinearGradientMode = def_FillGradientStyle

        Private _ParentCtl As BarGauge = Nothing


        'Enumerations
        Public Enum FillStyles As Integer
            Solid = 0
            Gradient = 1
        End Enum

        'Public Events
        Public Event PropertyChanged(ByVal PropertyName As String)

        'Public Interface
        Friend Sub New(ByVal Control As BarGauge)
            _ParentCtl = Control
        End Sub

        ''' <summary>
        ''' Defines the Background color of the gauge when <see cref="FillStyle"/> is set to <see cref="FillStyles">Solid</see>. Otherwise is used as the first color
        ''' in a color gradient with the end color determined by the <see cref="BackColor2"/> property.
        ''' </summary>
        ''' <value>The Background color of the gauge or the start gradient color of background.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <Description("The background color of the control. Primary color if FillStyle is set to Solid or gradient start color if FillStyle is set to Gradient."), _
            BrowsableAttribute(True)> _
        Public Property BackColor1() As Color
            Get
                Return _BackColor1
            End Get
            Set(ByVal value As Color)
                _BackColor1 = value
                If _BackColor1 = Color.Transparent AndAlso _FillStyle = FillStyles.Solid Then
                    _ParentCtl.SetTransparencyMode(True)
                Else
                    _ParentCtl.SetTransparencyMode(False)
                End If
                RaiseEvent PropertyChanged("BackColor1")
            End Set
        End Property
        ''' <summary>
        ''' Resets the <see>BarGauge.BackColor1</see> property.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub ResetBackColor1()
            BackColor1 = def_BackColor1
        End Sub
        ''' <summary>
        ''' Indicates whether the default value of the property has changed from it's default in design mode and needs to be serialized.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function ShouldSerializeBackColor1() As Boolean
            Return Not (BackColor1.Equals(def_BackColor1))
        End Function

        <Description("The secondary color of the control background when FillStyle is set to Gradient."), _
            BrowsableAttribute(True)> _
        Public Property BackColor2() As Color
            Get
                Return _BackColor2
            End Get
            Set(ByVal value As Color)
                _BackColor2 = value
                RaiseEvent PropertyChanged("BackColor2")
            End Set
        End Property
        Public Sub ResetBackColor2()
            BackColor2 = def_BackColor2
        End Sub
        Public Function ShouldSerializeBackColor2() As Boolean
            Return Not (BackColor2.Equals(def_BackColor2))
        End Function

        <Description("The FillStyle for the Background of this control."), _
                BrowsableAttribute(True)> _
        Public Property FillStyle() As FillStyles
            Get
                Return _FillStyle
            End Get
            Set(ByVal value As FillStyles)
                _FillStyle = value
                If _FillStyle = FillStyles.Solid AndAlso _BackColor1 = Color.Transparent Then
                    _ParentCtl.SetTransparencyMode(True)
                Else
                    _ParentCtl.SetTransparencyMode(False)
                End If
                RaiseEvent PropertyChanged("FillStyle")
            End Set
        End Property
        Public Sub ResetFillStyle()
            FillStyle = def_FillStyle
        End Sub
        Public Function ShouldSerializeFillStyle() As Boolean
            Return Not (FillStyle.Equals(def_FillStyle))
        End Function

        <Description("The gradient style of the control background when FillStyle is set to Gradient."), _
            BrowsableAttribute(True)> _
        Public Property FillGradientStyle() As LinearGradientMode
            Get
                Return _GradientOrientation
            End Get
            Set(ByVal value As LinearGradientMode)
                _GradientOrientation = value
                RaiseEvent PropertyChanged("FillGradientStyle")
            End Set
        End Property
        Public Sub ResetFillGradientStyle()
            FillGradientStyle = def_FillGradientStyle
        End Sub
        Public Function ShouldSerializeFillGradientStyle() As Boolean
            Return Not (FillGradientStyle.Equals(def_FillGradientStyle))
        End Function

        Public Sub Reset()
            _BackColor1 = def_BackColor1
            _BackColor2 = def_BackColor2
            _FillStyle = def_FillStyle
            _GradientOrientation = def_FillGradientStyle

            RaiseEvent PropertyChanged("All")
        End Sub
        Public Function ShouldSerialize() As Boolean
            If _BackColor1.Equals(def_BackColor1) _
                    AndAlso _BackColor2.Equals(def_BackColor2) _
                    AndAlso _FillStyle.Equals(def_FillStyle) _
                    AndAlso _GradientOrientation.Equals(def_FillGradientStyle) Then
                Return False
            Else
                Return True
            End If
        End Function

        Public Overrides Function ToString() As String
            Return "(Background)"
        End Function
    End Class
    Public Class BackgroundTypeConverter
        Inherits TypeConverter

        Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function
        Public Overrides Function GetProperties(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object, ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
            Return TypeDescriptor.GetProperties(GetType(BackgroundType))
        End Function
    End Class
#End Region

#Region "Axis Type"
    Public Class AxisType
        'Defaults
        Private Const def_ScaleMin As Double = -100
        Private Const def_ScaleMax As Double = 100

        Private def_AxisLabelDisplayStyle As LabelDisplayStyles = LabelDisplayStyles.All
        Private def_AxisLabelFont As Font = New System.Drawing.Font("Microsoft Sans Serif", 6.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Private def_AxisLabelFontColor As Color = Color.White
        Private Const def_AxisMajorTicks As Integer = 3
        Private Const def_AxisMinorTicks As Integer = 5
        Private def_AxisStyle As AxisStyles = AxisStyles.MajorMinorTicks
        Private def_AxisTickColor As Color = Color.Navy


        'Private Properties
        Private _ScaleMin As Double = def_ScaleMin
        Private _ScaleMax As Double = def_ScaleMax

        Private _AxisLabelDisplayStyle As LabelDisplayStyles = def_AxisLabelDisplayStyle
        Private _AxisLabelFont As Font = def_AxisLabelFont
        Private _AxisLabelFontColor As Color = def_AxisLabelFontColor
        Private _AxisMajorTicks As Integer = def_AxisMajorTicks
        Private _AxisMinorTicks As Integer = def_AxisMinorTicks
        Private _AxisStyle As AxisStyles = def_AxisStyle
        Private _AxisTickColor As Color = def_AxisTickColor

        Private _ParentCtl As BarGauge = Nothing


        'Events
        Public Event PropertyChanged(ByVal PropertyName As String)


        'Enumerations
        Public Enum LabelDisplayStyles As Integer
            NoLabels = 0
            EndPointsOnly = 1
            MajorTickLabelsOnly = 2
            All = 3
        End Enum
        Public Enum AxisStyles As Integer
            None = 0
            MajorTicksOnly = 1
            MajorMinorTicks = 2
        End Enum

        'Public Interface
        Friend Sub New(ByVal Control As BarGauge)
            _ParentCtl = Control
        End Sub

        <Description("Represents the scale Minimum."), _
            DefaultValue(def_ScaleMin), _
            BrowsableAttribute(True)> _
        Public Property Min() As Double
            Get
                Return _ScaleMin
            End Get
            Set(ByVal value As Double)
                _ScaleMin = value
                RaiseEvent PropertyChanged("Min")
            End Set
        End Property

        <Description("Represents the scale Maximum."), _
            DefaultValue(def_ScaleMax), _
            BrowsableAttribute(True)> _
        Public Property Max() As Double
            Get
                Return _ScaleMax
            End Get
            Set(ByVal value As Double)
                _ScaleMax = value
                RaiseEvent PropertyChanged("Max")
            End Set
        End Property

        <Description("The axis label display style."), _
        BrowsableAttribute(True)> _
        Public Property LabelDisplayStyle() As LabelDisplayStyles
            Get
                Return _AxisLabelDisplayStyle
            End Get
            Set(ByVal value As LabelDisplayStyles)
                _AxisLabelDisplayStyle = value
                RaiseEvent PropertyChanged("LabelDisplayStyle")
            End Set
        End Property
        Public Sub ResetLabelDisplayStyle()
            LabelDisplayStyle = def_AxisLabelDisplayStyle
        End Sub
        Public Function ShouldSerializeLabelDisplayStyle() As Boolean
            Return Not (LabelDisplayStyle.Equals(def_AxisLabelDisplayStyle))
        End Function

        <Description("The Font used for the Axis labels."), _
           BrowsableAttribute(True)> _
        Public Property Font() As Font
            Get
                Return _AxisLabelFont
            End Get
            Set(ByVal value As Font)
                _AxisLabelFont = value
                RaiseEvent PropertyChanged("Font")
            End Set
        End Property
        Public Sub ResetFont()
            Font = def_AxisLabelFont
        End Sub
        Public Function ShouldSerializeFont() As Boolean
            Return Not (Font.Equals(def_AxisLabelFont))
        End Function

        <Description("The Font Color used for the Axis labels."), _
           BrowsableAttribute(True)> _
        Public Property ForeColor() As Color
            Get
                Return _AxisLabelFontColor
            End Get
            Set(ByVal value As Color)
                _AxisLabelFontColor = value
                RaiseEvent PropertyChanged("ForeColor")
            End Set
        End Property
        Public Sub ResetForeColor()
            ForeColor = Me.def_AxisLabelFontColor
        End Sub
        Public Function ShouldSerializeForeColor() As Boolean
            Return Not (ForeColor.Equals(def_AxisLabelFontColor))
        End Function

        <Description("The Number of major ticks of the gauge Axis."), _
            DefaultValue(def_AxisMajorTicks), _
            BrowsableAttribute(True)> _
        Public Property MajorTicks() As Integer
            Get
                Return _AxisMajorTicks
            End Get
            Set(ByVal value As Integer)
                If value < 0 Then Throw New Exception("Number of Major Ticks must be at least 0.")
                _AxisMajorTicks = value
                RaiseEvent PropertyChanged("MajorTicks")
            End Set
        End Property

        <Description("The Number of minor ticks between each Major tick of the gauge Axis."), _
            DefaultValue(def_AxisMinorTicks), _
            BrowsableAttribute(True)> _
        Public Property MinorTicks() As Integer
            Get
                Return _AxisMinorTicks
            End Get
            Set(ByVal value As Integer)
                If value < 0 Then Throw New Exception("Number of Minor ticks must be at least 0.")
                _AxisMinorTicks = value
                RaiseEvent PropertyChanged("MinorTicks")
            End Set
        End Property

        <Description("The Axis display style."), _
            BrowsableAttribute(True)> _
        Public Property AxisStyle() As AxisStyles
            Get
                Return _AxisStyle
            End Get
            Set(ByVal value As AxisStyles)
                _AxisStyle = value
                RaiseEvent PropertyChanged("AxisStyle")
            End Set
        End Property
        Public Sub ResetAxisStyle()
            AxisStyle = def_AxisStyle
        End Sub
        Public Function ShouldSerializeAxisStyle() As Boolean
            Return Not (AxisStyle.Equals(def_AxisStyle))
        End Function

        <Description("The Axis tick mark color."), _
            BrowsableAttribute(True)> _
        Public Property TickColor() As Color
            Get
                Return _AxisTickColor
            End Get
            Set(ByVal value As Color)
                _AxisTickColor = value
                RaiseEvent PropertyChanged("TickColor")
            End Set
        End Property
        Public Sub ResetTickColor()
            TickColor = def_AxisTickColor
        End Sub
        Public Function ShouldSerializeTickColor() As Boolean
            Return Not (TickColor.Equals(def_AxisTickColor))
        End Function

        Public Overrides Function ToString() As String
            Return "(Scale)"
        End Function

        Public Sub Reset()
            _ScaleMin = def_ScaleMin
            _ScaleMax = def_ScaleMax

            _AxisLabelDisplayStyle = def_AxisLabelDisplayStyle
            _AxisLabelFont = def_AxisLabelFont
            _AxisLabelFontColor = def_AxisLabelFontColor
            _AxisMajorTicks = def_AxisMajorTicks
            _AxisMinorTicks = def_AxisMinorTicks
            _AxisStyle = def_AxisStyle
            _AxisTickColor = def_AxisTickColor

            RaiseEvent PropertyChanged("All")
        End Sub
        Public Function ShouldSerialize() As Boolean
            If _ScaleMin.Equals(def_ScaleMin) _
                    AndAlso _ScaleMax.Equals(def_ScaleMax) _
                    AndAlso _AxisLabelDisplayStyle.Equals(def_AxisLabelDisplayStyle) _
                    AndAlso _AxisLabelFont.Equals(def_AxisLabelFont) _
                    AndAlso _AxisLabelFontColor.Equals(def_AxisLabelFontColor) _
                    AndAlso _AxisMajorTicks.Equals(def_AxisMajorTicks) _
                    AndAlso _AxisMinorTicks.Equals(def_AxisMinorTicks) _
                    AndAlso _AxisStyle.Equals(def_AxisStyle) _
                    AndAlso _AxisTickColor.Equals(def_AxisTickColor) Then
                Return False
            Else
                Return True
            End If
        End Function
    End Class
    Public Class AxisTypeConverter
        Inherits TypeConverter

        Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function
        Public Overrides Function GetProperties(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object, ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
            Return TypeDescriptor.GetProperties(GetType(AxisType))
        End Function
    End Class
#End Region

#Region "Threshold"
    Public Class Threshold
        Private def_Value As Double
        Private def_Color As Color

        Private _Value As Double
        Private _Color As Color

        Public Event PropertyChanged(ByVal PropertyName As String)

        Friend Sub New()
            def_Value = Nothing
            def_Color = Nothing
            _Value = Nothing
            _Color = Nothing
        End Sub
        Friend Sub New(ByVal DefaultValue As Double, ByVal DefaultColor As Color)
            def_Value = DefaultValue
            _Value = DefaultValue
            def_Color = DefaultColor
            _Color = DefaultColor
        End Sub

        Public Property Value() As Double
            Get
                Return _Value
            End Get
            Set(ByVal value As Double)
                _Value = value
                RaiseEvent PropertyChanged("Value")
            End Set
        End Property
        Public Sub ResetValue()
            Value = def_Value
        End Sub
        Public Function ShouldSerializeValue() As Boolean
            Return Not (Value.Equals(def_Value))
        End Function

        Public Property Color() As Color
            Get
                Return _Color
            End Get
            Set(ByVal value As Color)
                _Color = value
                RaiseEvent PropertyChanged("Color")
            End Set
        End Property
        Public Sub ResetColor()
            Color = def_Color
        End Sub
        Public Function ShouldSerializeColor() As Boolean
            Return Not (Color.Equals(def_Color))
        End Function

        Public Sub Reset()
            _Value = def_Value
            _Color = def_Color
            RaiseEvent PropertyChanged("All")
        End Sub
        Public Function ShouldSerialize() As Boolean
            If Value.Equals(def_Value) AndAlso Color.Equals(def_Color) Then
                Return False
            Else
                Return True
            End If
        End Function

        Public Overrides Function ToString() As String
            Return "(Threshold)"
        End Function
    End Class
    Public Class ThresholdTypeConverter
        Inherits TypeConverter

        Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function
        Public Overrides Function GetProperties(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object, ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
            Return TypeDescriptor.GetProperties(GetType(Threshold))
        End Function
    End Class
#End Region

#Region "HeatLevel"
    Public Class HeatLevel
        Private def_StartValue As Double
        Private def_EndValue As Double
        Private def_StartColor As Color

        Private _StartValue As Double
        Private _EndValue As Double
        Private _StartColor As Color

        Private WithEvents _Threshold1 As Threshold
        Private WithEvents _Threshold2 As Threshold

        ''' <summary>
        ''' Defines the style of Heat Level to use in the Bar Gauge.
        ''' </summary>
        ''' <remarks>Use in conjunction with the HeatLevel Start/End Colors and threshold Values/Colors.
        ''' <br/><b>Note:</b>When using a single Heat Level, only the Left heat level properties will be used to define that heat level.</remarks>
        Public Enum HeatLevelStyles As Integer
            ''' <summary>
            ''' No Heat Level Displayed
            ''' </summary>
            ''' <remarks></remarks>
            None = 0
            ''' <summary>
            ''' Single Increasing Heat Level.
            ''' </summary>
            ''' <remarks></remarks>
            SingleIncreasing = 1
            ''' <summary>
            ''' Single decreasing Heat Level.
            ''' </summary>
            ''' <remarks></remarks>
            SingleDecreasing = 2
            ''' <summary>
            ''' Double Heat Levels that decrease towards middle.
            ''' </summary>
            ''' <remarks></remarks>
            DoubleDecreasingToMiddle = 3
            ''' <summary>
            ''' Double Heat Levels that increase towards middle.
            ''' </summary>
            ''' <remarks></remarks>
            DoubleIncreasingToMiddle = 4
        End Enum

        Public Event PropertyChanged(ByVal PropertyName As String)

        Friend Sub New(ByVal DefStartValue As Double, ByVal DefEndValue As Double, _
                       ByVal DefStartColor As Color, _
                       ByVal DefThreshold1Value As Double, ByVal DefThreshold2Value As Double, _
                       ByVal DefThreshold1Color As Color, ByVal DefThreshold2Color As Color)

            def_StartValue = DefStartValue
            def_EndValue = DefEndValue
            def_StartColor = DefStartColor
            _StartValue = def_StartValue
            _EndValue = def_EndValue
            _StartColor = def_StartColor

            _Threshold1 = New Threshold(DefThreshold1Value, DefThreshold1Color)
            _Threshold2 = New Threshold(DefThreshold2Value, DefThreshold2Color)
        End Sub
        Friend Sub New()
            _Threshold1 = New Threshold()
            _Threshold2 = New Threshold()
        End Sub

        Public Property StartValue() As Double
            Get
                Return _StartValue
            End Get
            Set(ByVal value As Double)
                _StartValue = value
                RaiseEvent PropertyChanged("StartValue")
            End Set
        End Property
        Public Sub ResetStartValue()
            StartValue = def_StartValue
        End Sub
        Public Function ShouldSerializeStartValue() As Boolean
            Return Not (StartValue.Equals(def_StartValue))
        End Function

        Public Property EndValue() As Double
            Get
                Return _EndValue
            End Get
            Set(ByVal value As Double)
                _EndValue = value
                RaiseEvent PropertyChanged("EndValue")
            End Set
        End Property
        Public Sub ResetEndValue()
            EndValue = def_EndValue
        End Sub
        Public Function ShouldSerializeEndValue() As Boolean
            Return Not (EndValue.Equals(def_EndValue))
        End Function

        Public Property StartColor() As Color
            Get
                Return _StartColor
            End Get
            Set(ByVal value As Color)
                _StartColor = value
                RaiseEvent PropertyChanged("StartColor")
            End Set
        End Property
        Public Sub ResetStartColor()
            _StartColor = def_StartColor
        End Sub
        Public Function ShouldSerializeStartColor() As Boolean
            Return Not (StartColor.Equals(def_StartColor))
        End Function

        <TypeConverter(GetType(ThresholdTypeConverter))> _
        Public Property Threshold1() As Threshold
            Get
                Return _Threshold1
            End Get
            Set(ByVal value As Threshold)
                _Threshold1 = value
                RaiseEvent PropertyChanged("Threshold1")
            End Set
        End Property
        Public Sub ResetThreshold1()
            _Threshold1.Reset()
        End Sub
        Public Function ShouldSerializeThreshold1() As Boolean
            Return _Threshold1.ShouldSerialize
        End Function

        <TypeConverter(GetType(ThresholdTypeConverter))> _
        Public Property Threshold2() As Threshold
            Get
                Return _Threshold2
            End Get
            Set(ByVal value As Threshold)
                _Threshold2 = value
                RaiseEvent PropertyChanged("Threshold2")
            End Set
        End Property
        Public Sub ResetThreshold2()
            _Threshold2.Reset()
        End Sub
        Public Function ShouldSerializeThreshold2() As Boolean
            Return _Threshold2.ShouldSerialize
        End Function

        Private Sub _Threshold1_PropertyChanged(ByVal PropertyName As String) Handles _Threshold1.PropertyChanged
            RaiseEvent PropertyChanged("Threshold1")
        End Sub

        Private Sub _Threshold2_PropertyChanged(ByVal PropertyName As String) Handles _Threshold2.PropertyChanged
            RaiseEvent PropertyChanged("Threshold2")
        End Sub

        Public Sub Reset()
            _StartValue = def_StartValue
            _EndValue = def_EndValue
            _StartColor = def_StartColor
            _Threshold1.Reset()
            _Threshold2.Reset()
        End Sub
        Public Function ShouldSerialize() As Boolean
            Return ShouldSerializeStartValue() _
                OrElse ShouldSerializeEndValue() _
                OrElse ShouldSerializeStartColor() _
                OrElse _Threshold1.ShouldSerialize _
                OrElse _Threshold2.ShouldSerialize
        End Function

        Public Overrides Function ToString() As String
            Return "(Heat Level)"
        End Function
    End Class
    Public Class HeatLevelTypeConverter
        Inherits TypeConverter

        Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function
        Public Overrides Function GetProperties(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object, ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
            Return TypeDescriptor.GetProperties(GetType(HeatLevel))
        End Function
    End Class
#End Region

End Class

